package com.jielin.handling.service.material.impl;

import com.alibaba.fastjson.JSON;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.jielin.handling.entity.material.MaterialFile;
import com.jielin.handling.entity.material.MaterialType;
import com.jielin.handling.entity.material.MaterialTypeDocument;
import com.jielin.handling.exception.ResultEnum;
import com.jielin.handling.mapper.material.MaterialFileMapper;
import com.jielin.handling.mapper.material.MaterialTypeDocumentMapper;
import com.jielin.handling.service.material.MaterialFileService;
import com.jielin.handling.service.xsky.XskyService;
import com.jielin.handling.util.ConstantUtil;
import com.jielin.handling.util.StringUtil;
import com.jielin.handling.util.VideoImageUtil;
import com.jielin.handling.util.pdfutil.PdfUtil;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.http.entity.ContentType;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FrameGrabber;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class MaterialFileServiceImpl implements MaterialFileService {
    @Autowired
    private MaterialFileMapper mFileMapper;
    @Autowired
    private MaterialTypeDocumentMapper mtdMapper;
    @Autowired
    private XskyService xskyService;

    @Override
    public List<MaterialFile> findFileByDocumentId(String documentid, String fileName) {
        return mFileMapper.findFileByDocumentId(documentid, fileName);
    }

    @Override
    public int addFile(MaterialFile materialFile) {
        if (materialFile == null) {
            return ResultEnum.FAIL.getCode();
        }
        return mFileMapper.addFile(materialFile);
    }

    final transient ReentrantLock lock = new ReentrantLock();

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public int delFileById(String fileid, String documentid) {
        if (StringUtil.isEmpty(fileid) || StringUtil.isEmpty(documentid)) {
            return ResultEnum.FAIL.getCode();
        }
        log.info("RecordDetailedService事务start...");
        int i = ResultEnum.FAIL.getCode();
        try {
            String[] fileIds = fileid.split(",");
            log.info("String[] fileIds==>" + JSON.toJSONString(fileIds));
            for (String id : fileIds) {
                if (StringUtil.isEmpty(id)) {
                    continue;
                }
                final ReentrantLock lock = this.lock;
                lock.lock();
                try {
                    mFileMapper.updateisdeleleById(id);
                    if (mtdMapper.findCountById(documentid) > 0) {
                        // 二级文件夹总数减1
                        mtdMapper.updateDocDelCount(documentid);
                    }
                } finally {
                    lock.unlock();
                }
            }
            i = 1;
        } catch (Exception e) {
            log.info("RecordDetailedService 事务回滚:{}", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return i;
    }

    @Override
    public MaterialFile findFileById(String fileId) {
        if (StringUtil.isEmpty(fileId)) {
            return null;
        }
        return mFileMapper.findInfoByFileId(fileId);
    }

    @Override
    public int updateFileName(String fileId, String newFileName) {
        return mFileMapper.updateFileName(fileId, newFileName);
    }

    @Override
    public List<MaterialFile> findPersonalAudit(String policeId) {
        return mFileMapper.findPersonalAudit(policeId);
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public int addDossierFile(MultipartFile[] files, String documentId
            , MaterialTypeDocument document, MaterialType mt, String fileString) {
        boolean isHaveFile = files != null && files.length > 0;
        // TODO xsky上传失败！ 添加 return ResultEnum.FAIL.getCode();
        // 表单上传文件或base64格式
        if (isHaveFile || !StringUtil.isEmpty(fileString)) {
            // 文件存放路径==》 案件名称-警员id-案件编号/嫌疑人姓名/一级目录名称/二级文件夹名称/
            String rootpath = mt.getCasename() + "-" + mt.getUploadpoliceid() + "-" + mt.getCasecode() + File.separator
                    + mt.getCaseSuspectName() + File.separator + mt.getMaterialtypename() + File.separator
                    + document.getDocumentname();

            // 源文件
            byte[] bytes = null;
            for (int i = 0; i < (isHaveFile ? files.length : 1); i++) {
                MultipartFile file = isHaveFile ? files[i] : null;

                // 文件原名称，去掉文件名中包含的空格
                String fileName = isHaveFile
                        ? file.getOriginalFilename().replaceAll("%20", "").replace(" ", "")
                        : StringUtil.generateRandomFilename();

                // 自定义的文件名称
                String trueFileName = isHaveFile ? fileName.substring(fileName.lastIndexOf("\\") + 1, fileName.lastIndexOf("."))
                        + "_" + StringUtil.generateRandomFilename() : fileName;

                // 设置新的Metadata
                ObjectMetadata meta = new ObjectMetadata();

                // 获取文件类型   高拍仪图片为jpg
                String fileType = isHaveFile ? fileName.substring(fileName.lastIndexOf(".")) : ".jpg";
                // 设置存放图片文件的路径
                String fileDocPath = rootpath + File.separator;
                String path = ConstantUtil.FILE_PATH + fileDocPath + trueFileName + fileType;
                // 兼容win
                path = path.replace("\\", "/");
                if (isHaveFile) {
                    try {
                        bytes = file.getBytes();
                        meta.setContentType(file.getContentType());
                        xskyService.uploadObjectForInputStream(ConstantUtil.XSKY_BUCKET_DOSSIER, path, bytes, meta);
                    } catch (Exception e) {
                        log.error(ResultEnum.XSKYUPLOADFAIL.getMsg(), e);
                        return ResultEnum.FAIL.getCode();
                    }
                } else {
                    try {
                        // Base64解码
                        BASE64Decoder decoder = new BASE64Decoder();
                        // 去掉头部（不能解析）
                        String base64String = fileString.substring(fileString.indexOf(",") + 1);
                        if (log.isDebugEnabled()) {
//                            log.info("Base64解码后:\n" + base64String);
                        }
                        bytes = decoder.decodeBuffer(base64String);
                        for (int j = 0; j < bytes.length; ++j) {
                            // 调整异常数据
                            if (bytes[j] < 0) {
                                bytes[j] += 256;
                            }
                        }
                        try {
                            meta.setContentType(ContentType.IMAGE_JPEG.getMimeType());
                            xskyService.uploadObjectForInputStream(ConstantUtil.XSKY_BUCKET_DOSSIER, path, bytes, meta);
                        } catch (Exception e) {
                            log.error(ResultEnum.XSKYUPLOADFAIL.getMsg(), e);
                            return ResultEnum.FAIL.getCode();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        log.error("base64解析失败！");
                        return ResultEnum.FAIL.getCode();
                    }
                }

                MaterialFile materialFile = MaterialFile.builder()
                        .id(StringUtil.generatorUUID())
                        .materialtypedocumentid(documentId)
                        .filename(trueFileName + fileType)
                        .url(ConstantUtil.XSKY_PREPATH_URL + path)
                        .filetype(fileType).build();

                // 缩略图文件路径
                String thumbnailpath = rootpath + File.separator;
                // 缩略图文件全路径
                String truepath = ConstantUtil.FILE_PATH + thumbnailpath + trueFileName + ConstantUtil.THUMBNAIL + VideoImageUtil.IMAGEMATPOINT;
                String trueJPGpath = ConstantUtil.FILE_PATH + thumbnailpath + trueFileName + ConstantUtil.THUMBNAIL + PdfUtil.SUFF_IMAGE;
                // 兼容win
                truepath = truepath.replace("\\", "/");
                trueJPGpath = trueJPGpath.replace("\\", "/");
                // 缩略图输入流
                ByteArrayInputStream byteArrayInputStream = null;
                // 缩略图输出流
                ByteArrayOutputStream byteArrayOutputStream = null;
                // 如果文件后缀是图片格式就生成缩略图
                if (StringUtil.isPHOTO(fileType)) {
                    // 生成缩略图
                    try {
                        byteArrayOutputStream = new ByteArrayOutputStream();
                        byteArrayInputStream = new ByteArrayInputStream(bytes);
                        Thumbnails.of(byteArrayInputStream).size(200, 300).toOutputStream(byteArrayOutputStream);

                        try {
                            meta.setContentType(ContentType.IMAGE_PNG.getMimeType());
                            xskyService.uploadObjectForInputStream(ConstantUtil.XSKY_BUCKET_DOSSIER, truepath, byteArrayOutputStream.toByteArray(), meta);
                        } catch (Exception e) {
                            log.error(ResultEnum.XSKYUPLOADFAIL.getMsg(), e);
                            return ResultEnum.FAIL.getCode();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        log.error("生成图片缩略图失败！");
                    }
                    // 设置缩略图路径
                    materialFile.setThumbnailpath(ConstantUtil.XSKY_PREPATH_URL + truepath);
                    // 如果文件后缀是视频格式就生成缩略图
                } else if (StringUtil.isVIDEO(fileType)) {
                    // 生成缩略图
                    String truepath2 = null;
                    try {
                        byteArrayOutputStream = new ByteArrayOutputStream();
                        byteArrayInputStream = new ByteArrayInputStream(bytes);
                        VideoImageUtil.randomGrabberFFmpegImage(
                                new FFmpegFrameGrabber(byteArrayInputStream), VideoImageUtil.MOD, byteArrayOutputStream);

                        try {
                            meta.setContentType(ContentType.IMAGE_PNG.getMimeType());
                            xskyService.uploadObjectForInputStream(ConstantUtil.XSKY_BUCKET_DOSSIER, truepath, byteArrayOutputStream.toByteArray(), meta);
                        } catch (Exception e) {
                            log.error(ResultEnum.XSKYUPLOADFAIL.getMsg(), e);
                            return ResultEnum.FAIL.getCode();
                        }
                    } catch (FrameGrabber.Exception e) {
                        e.printStackTrace();
                        log.error("生成视频缩略图失败！");
                    }
                    // 设置缩略图路径
                    materialFile.setThumbnailpath(ConstantUtil.XSKY_PREPATH_URL + (StringUtil.isEmpty(truepath2) ? truepath : truepath2));
                } else if (fileType.matches("^(.pdf|.PDF)$")) {
                    // 生成pdf缩略图
                    String thumbnailPath = null;
                    try {
                        byteArrayOutputStream = new ByteArrayOutputStream();
                        byteArrayInputStream = new ByteArrayInputStream(bytes);
                        PdfUtil.tranfer(
                                byteArrayInputStream, byteArrayOutputStream, PdfUtil.ZOOM);

                        try {
                            meta.setContentType(ContentType.IMAGE_JPEG.getMimeType());
                            xskyService.uploadObjectForInputStream(ConstantUtil.XSKY_BUCKET_DOSSIER, trueJPGpath, byteArrayOutputStream.toByteArray(), meta);
                        } catch (Exception e) {
                            log.error(ResultEnum.XSKYUPLOADFAIL.getMsg(), e);
                            return ResultEnum.FAIL.getCode();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("生成pdf缩略图失败！");
                    }
                    materialFile.setThumbnailpath(ConstantUtil.XSKY_PREPATH_URL + (StringUtil.isEmpty(thumbnailPath) ? trueJPGpath : thumbnailPath));
                    // 音频文件默认缩略图
                } else if (fileType.matches("^(.mp3|.MP3|.wav|.WAV)$")) {
                    materialFile.setThumbnailpath(ConstantUtil.WEB_MP3FILE_PATH);
                    // 其他文件默认缩略图
                } else {
                    materialFile.setThumbnailpath(ConstantUtil.WEB_DEFAULTFILE_PATH);
                }

                // 关闭输入输出流
                if (byteArrayOutputStream != null) {
                    try {
                        byteArrayOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (byteArrayOutputStream != null) {
                    try {
                        byteArrayOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    // 添加文件信息
                    if (mFileMapper.addFile(materialFile) > 0) {
                        // 文件夹数量加1信息
                        mtdMapper.updateDocAddCount(documentId);
                    }
                } catch (Exception e) {
                    log.info("添加文件信息事务回滚:{}", e);
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return ResultEnum.FAIL.getCode();
                }
            }
            return ResultEnum.SUCCESS.getCode();
        }
        return ResultEnum.FAIL.getCode();
    }

}
